/**
 * All Rights Reserved , Copyright (C) 2023 , 青岛鼎信通讯股份有限公司
 * <p>
 * CbbPmNoticeRecordService
 * 提醒记录表
 * <p>
 * 修改纪录
 * 2023-06-01 版本：1.0 hudeyong 创建。
 *
 * @version 版本：1.0
 * @author 作者：hudeyong
 * 创建日期：2023-06-01
 */
package com.topscomm.mis.cbb.pm.base.service.impl;

import cn.hutool.core.util.ObjectUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.topscomm.basic.BasicEntity;
import com.topscomm.cbo.pojo.CboApprovalFlowEntity;
import com.topscomm.cbo.pojo.CboApprovalNodeEntity;
import com.topscomm.cbo.pojo.CboDeptEntity;
import com.topscomm.cbo.pojo.CboUserEntity;
import com.topscomm.cbo.service.ICboApprovalFlowService;
import com.topscomm.cbo.service.ICboApprovalNodeService;
import com.topscomm.cbo.service.ICboDeptService;
import com.topscomm.cbo.service.ICboUserService;
import com.topscomm.mis.cbb.pm.base.dto.AppMessageDTO;
import com.topscomm.mis.cbb.pm.base.pojo.CbbPmNoticeRecordEntity;
import com.topscomm.mis.cbb.pm.base.pojo.CbbPmTaskEntity;
import com.topscomm.mis.cbb.pm.base.service.ICbbPmTaskService;
import com.topscomm.mis.cbb.pm.util.PmSystemConst;
import com.topscomm.mis.service.msgbus.IMsgBusProducerService;
import com.topscomm.pub.util.ConvertUtil;
import com.topscomm.pub.util.StringBufferProxy;
import com.topscomm.tap.common.ParameterCache;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;

import com.topscomm.mis.cbb.pm.base.service.ICbbPmNoticeRecordService;
import com.topscomm.mis.cbb.pm.base.service.impl.auto.CbbPmNoticeRecordServiceAuto;

import java.io.IOException;
import java.time.LocalDate;
import java.time.ZoneId;
import java.util.*;
import java.util.stream.Collectors;

@Service(value = "cbbPmNoticeRecordService")
@Scope(value = "singleton")
public class CbbPmNoticeRecordService extends CbbPmNoticeRecordServiceAuto implements ICbbPmNoticeRecordService {

    @Autowired
    private ICbbPmTaskService cbbPmTaskService;

    @Autowired
    private ICbbPmNoticeRecordService cbbPmNoticeRecordService;

    @Autowired
    private ICboApprovalFlowService cboApprovalFlowService;

    @Autowired
    private ICboApprovalNodeService cboApprovalNodeService;

    @Autowired
    private IMsgBusProducerService msgBusProducerService;

    @Autowired
    private ICboUserService cboUserService;

    @Autowired
    private ICboDeptService cboDeptService;

    /**
     * @return
     * @Author wangjielei
     * @Description 发送消息提醒
     * @Date 2023/6/25 11:36
     * @Param
     **/
    @Override
    public void sendNotice() throws IOException {
        List<BasicEntity> cbbPmNoticeRecordEntityList = new ArrayList<>();
        List<AppMessageDTO> appMessageDTOList = new ArrayList<>();
        // 处理日程消息提醒
        dealScheduleNotice(cbbPmNoticeRecordEntityList, appMessageDTOList);
        // 处理待进行消息提醒
        dealReadyStartNotice(cbbPmNoticeRecordEntityList, appMessageDTOList);
        // 处理超期消息提醒
        dealOverdueNotice(cbbPmNoticeRecordEntityList, appMessageDTOList);
        // 批量保存提醒记录表数据
        if(!CollectionUtils.isEmpty(cbbPmNoticeRecordEntityList)){
            cbbPmNoticeRecordService.insertBatch(cbbPmNoticeRecordEntityList);

            int batchSize = 500; // 每批次的大小
            int totalSize = appMessageDTOList.size();
            int batchCount = (int) Math.ceil((double) totalSize / batchSize); // 计算批次的总数
            for (int i = 0; i < batchCount; i++) {
                int fromIndex = i * batchSize;
                int toIndex = Math.min(fromIndex + batchSize, totalSize);
                List<AppMessageDTO> batchList = appMessageDTOList.subList(fromIndex, toIndex);
                // 调用推送消息的方法
                msgBusProducerService.pushMsg("CboMessageSys", "INSERT", batchList);
            }
        }
    }

    /**
     * @return
     * @Author wangjielei
     * @Description 处理日程消息提醒
     * @Date 2023/6/25 18:14
     * @Param
     **/
    private void dealScheduleNotice(List<BasicEntity> cbbPmNoticeRecordEntityList, List<AppMessageDTO> appMessageDTOList) throws IOException {
    }

    /**
     * @return void
     * @Author wangjielei
     * @Description 处理待进行消息提醒
     * @Date 2023/6/25 18:15
     * @Param []
     **/
    private void dealReadyStartNotice(List<BasicEntity> cbbPmNoticeRecordEntityList, List<AppMessageDTO> appMessageDTOList) throws IOException {
        // 查询所有当前审批流程节点中审批参数包含待进行消息审批节点
        List<CboApprovalNodeEntity> cboApprovalNodeEntityList = queryAllNodeEntity(PmSystemConst.CSIFLOW, PmSystemConst.messageType.READY_START_NOTICE);
        List<String> nodeidList = cboApprovalNodeEntityList.stream().map(entity -> ConvertUtil.convertToString(entity.getId())).collect(Collectors.toList());
        // 查询未发送消息的待进行任务
        StringBufferProxy sql = new StringBufferProxy();
        sql.appendSingle(" deliverystatus = '1' AND status = '{0}' and code not in (select taskcode from CBB_PM_NOTICE_RECORD where noticetype = '{1}' ) ",
                PmSystemConst.TaskStatus.READYSTART, PmSystemConst.NoticeType.READYSTART);
        List<CbbPmTaskEntity> unNoticeEntityList = cbbPmTaskService.queryByWhere(sql.toString());
        // 筛选配置了发消息提醒节点的待进行任务
        unNoticeEntityList = unNoticeEntityList.stream().filter(entity -> nodeidList.contains(entity.getNodeid())).collect(Collectors.toList());
        if (unNoticeEntityList.size() > 0) {
            for (CbbPmTaskEntity entity : unNoticeEntityList) {
                // 流程节点
                String nodeid = entity.getNodeid();
                Optional<CboApprovalNodeEntity> optionalNodeEntity = cboApprovalNodeEntityList.stream().filter(node -> ConvertUtil.convertToString(node.getId()).equals(nodeid)).findFirst();
                // 没有找到符合条件的节点实体
                if (!optionalNodeEntity.isPresent()) {
                    continue;
                }
                // 找到符合条件的节点实体
                CboApprovalNodeEntity nodeEntity = optionalNodeEntity.get();
                String nodeName = nodeEntity.getName();
                String approvalParam = ConvertUtil.convertToString(nodeEntity.getApprovalparam());
                ObjectMapper objectMapper = new ObjectMapper();
                Map<String, Object> approvalParamMap = objectMapper.readValue(approvalParam, Map.class);
                int days = ConvertUtil.convertToInt(approvalParamMap.get(PmSystemConst.messageType.READY_START_NOTICE));
                // 获取计划开始日期
                Date planbegintime = entity.getPlanbegintime();
                // 计划开始日期+days
                Date planBeginTimePlusTwoDays = computeDate(planbegintime, days);
                // 今天 是否是 计划开始日期+days
                boolean isSameDay = isSameDay(planBeginTimePlusTwoDays, new Date());
                // 组装消息记录实体
                if (isSameDay) {
                    CbbPmNoticeRecordEntity cbbPmNoticeRecordEntity = assembleCbbPmNoticeRecordEntity(entity, PmSystemConst.NoticeType.READYSTART);
                    cbbPmNoticeRecordEntityList.add(cbbPmNoticeRecordEntity);
                    // 组装OA推送消息实体
                    AppMessageDTO appMessageDTO = assembleReadyStartMessage(entity, nodeName, days);
                    appMessageDTOList.add(appMessageDTO);
                    // 胡正明 和 一级部门长
                    List<AppMessageDTO> leaderList = assembleReadyStartLeaderMessage(entity, nodeName, days);
                    appMessageDTOList.addAll(leaderList);
                }
            }
        }
    }

    /**
     * @return void
     * @Author wangjielei
     * @Description 处理超期消息提醒
     * @Date 2023/6/25 18:15
     * @Param []
     **/
    private void dealOverdueNotice(List<BasicEntity> cbbPmNoticeRecordEntityList, List<AppMessageDTO> appMessageDTOList) throws IOException {
        // 查询所有当前审批流程节点中审批参数包含超期消息审批节点
        List<CboApprovalNodeEntity> cboApprovalNodeEntityList = queryAllNodeEntity(PmSystemConst.CSIFLOW, PmSystemConst.messageType.OVERDUE_NOTICE);
        List<String> nodeidList = cboApprovalNodeEntityList.stream().map(entity -> ConvertUtil.convertToString(entity.getId())).collect(Collectors.toList());
        // 查询所有未发送消息的未开始、待进行、进行中的任务
        StringBufferProxy sql = new StringBufferProxy();
        sql.appendSingle(" deliverystatus = '1' AND status in ('{0}', '{1}', '{2}') and code not in (select taskcode from CBB_PM_NOTICE_RECORD where noticetype = '{3}' )",
                PmSystemConst.TaskStatus.NOTSTART, PmSystemConst.TaskStatus.READYSTART, PmSystemConst.TaskStatus.RUNNING, PmSystemConst.NoticeType.OVERDUE);
        List<CbbPmTaskEntity> unNoticeEntityList = cbbPmTaskService.queryByWhere(sql.toString());
        // 筛选配置了超期提醒节点的任务
        unNoticeEntityList = unNoticeEntityList.stream().filter(entity -> nodeidList.contains(entity.getNodeid())).collect(Collectors.toList());
        if (unNoticeEntityList.size() > 0) {
            for (CbbPmTaskEntity entity : unNoticeEntityList) {
                // 流程节点
                String nodeid = entity.getNodeid();
                Optional<CboApprovalNodeEntity> optionalNodeEntity = cboApprovalNodeEntityList.stream().filter(node -> ConvertUtil.convertToString(node.getId()).equals(nodeid)).findFirst();
                // 没有找到符合条件的节点实体
                if (!optionalNodeEntity.isPresent()) {
                    continue;
                }
                // 找到符合条件的节点实体
                CboApprovalNodeEntity nodeEntity = optionalNodeEntity.get();
                String nodeName = nodeEntity.getName();
                // 获取计划结束日期
                Date planendtime = entity.getPlanendtime();
                if(ObjectUtil.isEmpty(planendtime)){
                    continue;
                }
                LocalDate localDate = planendtime.toInstant().atZone(ZoneId.systemDefault()).toLocalDate(); // 将Date对象转换为LocalDate对象
                LocalDate today = LocalDate.now(); // 当前日期
                // 比较当前日期是否晚于计划结束日期，只比较年月日部分
                boolean isAfter = today.isAfter(localDate);
                // 组装消息记录实体
                if (isAfter) {
                    CbbPmNoticeRecordEntity cbbPmNoticeRecordEntity = assembleCbbPmNoticeRecordEntity(entity, PmSystemConst.NoticeType.OVERDUE);
                    cbbPmNoticeRecordEntityList.add(cbbPmNoticeRecordEntity);
                    // 组装OA推送消息实体
                    AppMessageDTO appMessageDTO = assembleOverdueMessage(entity, nodeName);
                    appMessageDTOList.add(appMessageDTO);
                    // 胡正明 和 一级部门长
                    AppMessageDTO leaderAppMessageDTO = assembleOverdueLeaderMessage(entity, nodeName);
                    appMessageDTOList.add(leaderAppMessageDTO);
                }
            }
        }
    }

    /**
     * @return com.topscomm.mis.cbb.pm.base.dto.AppMessageDTO
     * @Author wangjielei
     * @Description 任务超期 领导消息实体组装
     * @Date 2023/6/27 11:06
     * @Param [entity, nodeName]
     **/
    private AppMessageDTO assembleOverdueLeaderMessage(CbbPmTaskEntity entity, String nodeName) {
        String usercode = entity.getResponsibleusercode();
        String username = entity.getResponsibleusername();
        CboUserEntity headerUser = queryFirstHeader(usercode);
        String headercode = headerUser.getCode();
        AppMessageDTO appMessageDTO = new AppMessageDTO();
        appMessageDTO.setSourceid(ConvertUtil.convertToString(entity.getId()));
        appMessageDTO.setMessagetype(PmSystemConst.sendMessageType.PUSH_NOTIFICATIONS);
        appMessageDTO.setSourcetype(CbbPmTaskEntity.tableName);
        appMessageDTO.setTitle("元器件抽检任务超期！");
        StringBufferProxy content = new StringBufferProxy();
        content.appendSingle("{0}[{1}]负责的{2}已超期。", username, usercode, nodeName);
        appMessageDTO.setContent(content.toString());
        appMessageDTO.setBanner(content.toString());
        appMessageDTO.setReceivecode(headercode);
        return appMessageDTO;
    }

    /**
     * @return com.topscomm.mis.cbb.pm.base.dto.AppMessageDTO
     * @Author wangjielei
     * @Description 任务超期 本人消息实体组装
     * @Date 2023/6/27 11:07
     * @Param [entity, nodeName]
     **/
    private AppMessageDTO assembleOverdueMessage(CbbPmTaskEntity entity, String nodeName) {
        AppMessageDTO appMessageDTO = new AppMessageDTO();
        appMessageDTO.setSourceid(ConvertUtil.convertToString(entity.getId()));
        appMessageDTO.setMessagetype(PmSystemConst.sendMessageType.PUSH_NOTIFICATIONS);
        appMessageDTO.setSourcetype(CbbPmTaskEntity.tableName);
        appMessageDTO.setTitle("元器件抽检任务超期！");
        StringBufferProxy content = new StringBufferProxy();
        content.appendSingle("您所负责的{0}已超期，请尽快登陆营销管理平台完成任务。", nodeName);
        appMessageDTO.setContent(content.toString());
        appMessageDTO.setBanner(content.toString());
        appMessageDTO.setReceivecode(ConvertUtil.convertToString(entity.getResponsibleusercode()));
        return appMessageDTO;
    }

    /**
     * @return com.topscomm.mis.cbb.pm.base.dto.AppMessageDTO
     * @Author wangjielei
     * @Description 组装消息实体 -- 胡正明 和 一级部门长
     * @Date 2023/6/26 19:48
     * @Param [entity, nodeName, days]
     **/
    private List<AppMessageDTO> assembleReadyStartLeaderMessage(CbbPmTaskEntity entity, String nodeName, int days) {
        String usercode = entity.getResponsibleusercode();
        String username = entity.getResponsibleusername();
        List<AppMessageDTO> leaderList = new ArrayList<>();
        List<String> leaders = new ArrayList<>();
        // 获取系统参数配置的人员
        String cbbNoticeRecipients = ParameterCache.getValue("cbbNoticeRecipients", "0108");
        if (!usercode.equals(cbbNoticeRecipients)) {
            leaders.add(cbbNoticeRecipients);
        }
        CboUserEntity headerUser = queryFirstHeader(usercode);
        // 本人如果是一级部门长则不再重复发送消息
        if (!headerUser.getCode().equals(usercode)) {
            leaders.add(headerUser.getCode());
        }
        if (leaders.size() > 0) {
            for (String headercode : leaders) {
                AppMessageDTO appMessageDTO = new AppMessageDTO();
                appMessageDTO.setSourceid(ConvertUtil.convertToString(entity.getId()));
                appMessageDTO.setMessagetype(PmSystemConst.sendMessageType.PUSH_NOTIFICATIONS);
                appMessageDTO.setSourcetype(CbbPmTaskEntity.tableName);
                appMessageDTO.setTitle("元器件抽检审批超期！");
                StringBufferProxy content = new StringBufferProxy();
                content.appendSingle("{0}[{1}]负责的{2}已超期{3}天。", username, usercode, nodeName, days);
                appMessageDTO.setContent(content.toString());
                appMessageDTO.setBanner(content.toString());
                appMessageDTO.setReceivecode(headercode);
                leaderList.add(appMessageDTO);
            }
        }
        return leaderList;
    }

    /**
     * @return com.topscomm.cbo.pojo.CboUserEntity
     * @Author wangjielei
     * @Description 根据用户usercode获取其一级部门长user对象
     * @Date 2023/6/27 10:57
     * @Param [usercode]
     **/
    private CboUserEntity queryFirstHeader(String usercode) {
        // 获取一级部门
        CboUserEntity user = cboUserService.queryObjectByWhere(" code = '" + usercode + "'");
        String deptid = ConvertUtil.convertToString(user.getDeptid());
        CboDeptEntity dept = cboDeptService.queryById(deptid);
        String code = dept.getCode();
        String firstCode = code.contains("_") ? code.substring(0, code.indexOf("_") + 5) : code.substring(0, 4);
        CboDeptEntity firstdept = cboDeptService.queryObjectByWhere(" code = '" + firstCode + "'");
        String headerid = firstdept.getHeaderids();
        CboUserEntity headerUser = cboUserService.queryById(headerid);
        return headerUser;
    }

    /**
     * @return com.topscomm.mis.cbb.pm.base.dto.AppMessageDTO
     * @Author wangjielei
     * @Description 组装消息实体 -- 负责人
     * @Date 2023/6/26 19:48
     * @Param [entity, nodeName, days]
     **/
    private AppMessageDTO assembleReadyStartMessage(CbbPmTaskEntity entity, String nodeName, int days) {
        AppMessageDTO appMessageDTO = new AppMessageDTO();
        appMessageDTO.setSourceid(ConvertUtil.convertToString(entity.getId()));
        appMessageDTO.setMessagetype(PmSystemConst.sendMessageType.PUSH_NOTIFICATIONS);
        appMessageDTO.setSourcetype(CbbPmTaskEntity.tableName);
        appMessageDTO.setTitle("元器件抽检审批超期！");
        StringBufferProxy content = new StringBufferProxy();
        content.appendSingle("您所负责的{0}已超期{1}天，请尽快登陆营销管理平台进行处理。", nodeName, days);
        appMessageDTO.setContent(content.toString());
        appMessageDTO.setBanner(content.toString());
        appMessageDTO.setReceivecode(ConvertUtil.convertToString(entity.getResponsibleusercode()));
        return appMessageDTO;
    }

    /**
     * @return java.util.List<com.topscomm.cbo.pojo.CboApprovalNodeEntity>
     * @Author wangjielei
     * @Description 查询某一目录下所有流程的所有节点
     * @Date 2023/6/26 9:51
     * @Param [csiflow]
     **/
    private List<CboApprovalNodeEntity> queryAllNodeEntity(String csiflow, String messageType) {
        List<CboApprovalNodeEntity> cboApprovalNodeEntityList = new ArrayList<>();
        StringBufferProxy sql = new StringBufferProxy();
        sql.appendSingle(" code = '{0}' ", csiflow);
        CboApprovalFlowEntity cboApprovalFlowEntity = cboApprovalFlowService.queryObjectByWhere(sql.toString());
        sql.clear();
        if (ObjectUtil.isEmpty(cboApprovalFlowEntity)) {
            return cboApprovalNodeEntityList;
        }
        String parentid = ConvertUtil.convertToString(cboApprovalFlowEntity.getId());
        sql.appendSingle(" APPROVALPARAM LIKE '%\"{0}\"%' AND FLOWID IN (SELECT id FROM CBOAPPROVALFLOW WHERE parentid = '{1}') ", messageType, parentid);
        cboApprovalNodeEntityList = cboApprovalNodeService.queryByWhere(sql.toString());
        return cboApprovalNodeEntityList;
    }

    /**
     * @return java.util.Date
     * @Author wangjielei
     * @Description 计算日期
     * @Date 2023/6/25 20:27
     * @Param [planbegintime, i]
     **/
    private Date computeDate(Date plantime, int i) {
        Calendar cal = Calendar.getInstance();
        cal.setTime(plantime);
        cal.add(Calendar.DATE, i);
        return cal.getTime();
    }

    /**
     * @return boolean
     * @Author wangjielei
     * @Description 判断两个日期是否是同一天
     * @Date 2023/6/25 20:25
     * @Param [date1, date2]
     **/
    public static boolean isSameDay(Date date1, Date date2) {
        Calendar cal1 = Calendar.getInstance();
        cal1.setTime(date1);
        int year1 = cal1.get(Calendar.YEAR);
        int month1 = cal1.get(Calendar.MONTH);
        int day1 = cal1.get(Calendar.DAY_OF_MONTH);

        Calendar cal2 = Calendar.getInstance();
        cal2.setTime(date2);
        int year2 = cal2.get(Calendar.YEAR);
        int month2 = cal2.get(Calendar.MONTH);
        int day2 = cal2.get(Calendar.DAY_OF_MONTH);

        return year1 == year2 && month1 == month2 && day1 == day2;
    }

    /**
     * @return com.topscomm.mis.cbb.pm.base.pojo.CbbPmNoticeRecordEntity
     * @Author wangjielei
     * @Description 组装消息提醒记录对象
     * @Date 2023/6/26 10:06
     * @Param [entity]
     **/
    private CbbPmNoticeRecordEntity assembleCbbPmNoticeRecordEntity(CbbPmTaskEntity entity, int noticeType) {
        CbbPmNoticeRecordEntity cbbPmNoticeRecordEntity = new CbbPmNoticeRecordEntity();
        cbbPmNoticeRecordEntity.setProjectuucode(entity.getProjectuucode());
        cbbPmNoticeRecordEntity.setWbscode(entity.getWbscode());
        cbbPmNoticeRecordEntity.setTaskcode(entity.getCode());
        cbbPmNoticeRecordEntity.setNoticetype(noticeType);
        cbbPmNoticeRecordEntity.setNoticetime(new Date());
        return cbbPmNoticeRecordEntity;
    }
}
